//-------------------------------------------------------------------------
//
//  Copyright (c) 1999 Cornell University
//  Computer Systems Laboratory
//  Cornell University, Ithaca, NY 14853
//  All Rights Reserved
//
//  Permission to use, copy, modify, and distribute this software
//  and its documentation for any purpose and without fee is hereby
//  granted, provided that the above copyright notice appear in all
//  copies. Cornell University makes no representations
//  about the suitability of this software for any purpose. It is
//  provided "as is" without express or implied warranty. Export of this
//  software outside of the United States of America may require an
//  export license.
//
//  $Id: cpu.v,v 1.1.1.1 2003/01/16 19:49:43 heinrich Exp $
//
//-------------------------------------------------------------------------

`include "mips.h"

module	cpu
(
	CLK,				// Clock signals
	Daddr, Dread, Dwrite, Dout, Dsize,
	Din, Iaddr, Iin,		// Memory interface
        MRST				// Master reset
);

//--------------------------------------------------------------------

input		CLK;			// Clock 
input	[31:0]	Din;			// Read path to data cache
input	[31:0]	Iin;			// Instruction input
input		MRST;			// Master reset

output  [31:0]	Daddr;			// Address for data access
output	[31:0]	Dout;			// Write path to data cache
output		Dread;			// Data read
output		Dwrite;			// Data write enable
output	[1:0]	Dsize;
output	[31:0]	Iaddr;			// Address for instruction access

/*--------------------------------------------------------------------
Global Signals
---------------------------------------------------------------------*/
// IF
reg	[31:0] 	PC, savedBranchPC;	// PC chain
reg	[31:0]  PC1,PC2, PC3, PC4;
reg	[31:0]  IR1, IR2, IR3, IR4; 	// IR chain
wire	[4:0]  	PCsel;			// Sel control for PC
wire	[31:0] 	PCoffset;		// Offset value for branches
wire	[25:0] 	PCjump;			// Destination address for jumps
wire 	[31:0] 	PCbr, PCinc, PCjmp;
reg 	inDelaySlot;
// RD
wire 	[4:0]	RSaddr;			// Source reg address specifier
wire 	[4:0]	RTaddr;			// Target reg address specifier
wire 	[31:0]	Imm;			// Immediate data
reg	[31:0] 	ImmReg;			// Immediate bus
wire	[31:0]	RSbus; 			// S source bus
reg	[31:0]  RSreg;
wire	[31:0] 	RTbus;			// T source bus
reg	[31:0]	RTreg;

wire		instIsSyscall;		// syscall inst in decode
reg		rInstIsSyscall;		// one cycle later
reg		rrInstIsSyscall;	// one cycle later
wire		decodeStall;		// should I stall the pipe?

wire		instIsMult;		// syscall inst in decode
reg		rInstIsMult;		// one cycle later
reg		rrInstIsMult;	// one cycle later
wire		multStall;

// EX

wire  	[31:0] 	ALUout;			// pre ALU output register
wire	[31:0]  ALUoutReg;
wire	       	QCResult;		// Result from quick compare logic
//reg		QCResultreg;
wire	[5:0]  	QCsel;
wire 	[7:0]  	ALUsel;
wire	       	UseImm;
//reg		UseImmReg;
//wire		instIsBranch;


wire [31:0] RSbypass;
wire [31:0] RTbypass;

// MEM
reg 	[31:0] 	MAR, LMDR, SMDR;	// Mem address and data registers
reg	[31:0] MAR2;
reg	    [31:0]  loadData;
reg	    [31:0]  preLoadData;
wire [4:0] RDaddrm;
wire [2:0] WBselaux;

// WB
reg	     [31:0]	WBreg;			// write back register
wire     [4:0]	RDaddr;			// Destination register address
reg	 [4:0]  RDaddrReg;
wire     [2:0]	WBsel;			// Stage 5 result select
wire           	WriteCLK;              	// Write clock	

wire	[31:0] 	RDbus;			// Destination bus
reg	[31:0]  RDreg;
reg [31:0] 	lastPC;
   
initial  begin
//   $monitor("%d: MRST = %b\n", $time, MRST);
//   $monitor("time=%d, pc=%h, RTaddr=%h, state=%h, RSbus=%h, RTbus=%h\n", $time, PC, RTaddr, State, RSbus, RTbus);
//   $monitor("time = %d, WBsel = %h, PCinc = %h, WBreg = %h\n", $time,
//      WBsel, PCinc, WBreg);
//   $monitor("%d:\n ###########  WriteClk = %b #############\n\n", $time,
//      WriteCLK);
//   $monitor("%d: IR4 op = %h, dest1=%h, dest2=%h\n", $time, IR4[`op],
//      IR4[`rt], IR4[`rd]);
//   $monitor("%d: State = %d\n", $time, State);
//   $monitor("%d: NextState = %d\n", $time, NextState);

end 

reg	[31:0]	num_instructions;

// -------------------------------------------------------------------


// FLOPs that reset the machine to a usable state, and then flow through 
// the pipe.  The instruction register is flopped for each pipeline stage
// and any control signals for that stage should come from that stages
// version of the instruction register.

always @(posedge CLK) begin

/*
	$display("PC: %h",PC);
	
	$disasm(IR4);
	$disasm(IR3);	
	$display("Executing:");
	$disasm(IR2);	
	$disasm(IR1);
	$disasm(Iin);
*/

   if (MRST) begin
      num_instructions <= `TICK 32'b0;
      IR1 <= `TICK 32'b0;
      IR2 <= `TICK 32'b0;
      IR3 <= `TICK 32'b0;
      IR4 <= `TICK 32'b0;
   end
   else begin

//BEGIN NORMAL INSTRUCTION UPDATES
      if(decodeStall || multStall) begin

     // IR1 <= `TICK Iin;
      //PC1 <= `TICK PC;

      IR2 <= `TICK 32'b00000000000000000000000000000000;
      //PC2 <= `TICK PC1;

      IR3 <= `TICK IR2;
      PC3 <= `TICK PC2;

      IR4 <= `TICK IR3;
      PC4 <= `TICK PC3;
      
      end 
      else begin
      num_instructions <= `TICK num_instructions + 1;
      IR1 <= `TICK Iin;
      PC1 <= `TICK PC;

      IR2 <= `TICK IR1;
      PC2 <= `TICK PC1;

      IR3 <= `TICK IR2;
      PC3 <= `TICK PC2;

      IR4 <= `TICK IR3;
      PC4 <= `TICK PC3;
      end
   end

  /* if ((State == `EX_STATE) && (inDelaySlot) && (instIsBranch)) begin
      $display("Branches are not allowed in delay slots!\n");
      $display("PC is %h, inst is:\n", PC);
      $disasm(IR2);
      $finish;
   end*/
end

/*--------------------------------------------------------------------
Instruction Fetch Stage
---------------------------------------------------------------------*/

ifcontr IFcontr 			// IF control logic
(
	.I2 (IR2),
	.QC (QCResult),
	.PCsel (PCsel), 
	.PCoffset (PCoffset), 
	.PCjump (PCjump)
); 						


/*--------------------------------------------------------------------
PCsection: Update program counter and keep PC and Instruction register
           chains.  In the pipelined MIPS, the PC is updated in the
           EX phase.
---------------------------------------------------------------------*/

// Virtual instruction address taken from PC
assign Iaddr = PC;	
assign PCbr  = PC2 + 4 + PCoffset;
assign PCinc = PC + 32'h00000004;
assign PCjmp = {PC2[31:28],PCjump,2'b0};



// PC is now flopped on negedge CLK in an attempt to emulate the way
// the MIPS R3000 shifts the IF stage by half a phase.  In the pipelined
// model you can think of this like the PC gets flopped half-way through
// the EX phase.  The value will either be the PC+4 or the branch target.

always @(negedge CLK) begin
   if (MRST) begin
      PC <= `TICK 32'hbfc00000;				// Reset vector
     // inDelaySlot <= `TICK 1'b0;
   end
   else if ((!decodeStall) && (!multStall)) begin

	    case(PCsel)		     
	       `select_pc_add:	    PC <= `TICK PCbr;
	       `select_pc_jump:     PC <= `TICK PCjmp;
	       `select_pc_register: PC <= `TICK RSbypass; //mux
		default: PC <= `TICK PCinc;
	    endcase 

	 // Here "inst" refers to the previous instruction
	// inDelaySlot <= `TICK instIsBranch;
      end
end



/*--------------------------------------------------------------------
RF read stage  (RD) -- Read operands from register file
---------------------------------------------------------------------*/

rf regfile		// Register File
(	
	.WCLK	(WriteCLK),
	.RSaddr	(RSaddr), 
	.RTaddr	(RTaddr), 
	.RDaddr	(RDaddr), 
	.RS	(RSbus), 
	.RT	(RTbus),
	.RD	(RDbus),
	.UseImm	(UseImm)
);

rd RDcontr		// Register File and immediate control logic
(
	.I1	(IR1),
     	.RSaddr	(RSaddr),
     	.RTaddr	(RTaddr),
     	.Imm	(Imm),
	.instIsSyscall (instIsSyscall),
	.instIsMult (instIsMult)
);

// Register File Write Clock
assign WriteCLK = (CLK == 1'b0);

// Flop the Imm field from Decode for use in the EX stage
always @(posedge CLK) begin
   ImmReg <= `TICK Imm;
	RSreg<=`TICK RSbus;
	RTreg<=`TICK RTbus;
end

// -------------------------------------------------------------------
// This section is code that detects syscall instructions and stalls
// them in the Decode stage for 2 cycles.  This will become necessary
// in our pipelined model so that our syscall emulation routines will
// function properly.  Do not change this code unless you have a 
// *really* good reason
// -------------------------------------------------------------------

always @(posedge CLK) begin
   rInstIsSyscall <= `TICK instIsSyscall;
   rrInstIsSyscall <= `TICK rInstIsSyscall;
end

assign decodeStall = (instIsSyscall | rInstIsSyscall) & !rrInstIsSyscall;

// -------------------------------------------------------------------

// mult stall?
always @(posedge CLK) begin
   rInstIsMult <= `TICK instIsMult;
   rrInstIsMult <= `TICK rInstIsMult;
end

assign multStall = (instIsMult | rInstIsMult) & !rrInstIsMult;

/*--------------------------------------------------------------------
Execute Stage (EX) -- Perform ALU or SHIFT Operation
---------------------------------------------------------------------*/
wb WBcontrolaux
(
	.I4	(IR3),
     	.RDaddr (RDaddrm),
     	.WBsel	(WBselaux)
);
bp BP(
	.I2 (IR2),
	.Memaddr(RDaddrm),
	.WBaddr(RDaddr),
	.Memresult(MAR),
	.WBresult(WBreg),
	.RSreg(RSreg),
	.RTreg(RTreg),
	.RTbypassed(RTbypass),
	.RSbypassed(RSbypass)
);
	

ex EXcontr		// ALU control
(
	.I2		(IR2),
        .QCsel		(QCsel),
        .ALUsel		(ALUsel),
        .UseImm		(UseImm)
);

alu	ALU	
(
	.RSbus	(RSbypass), //mux
	.RTbus	(RTbypass),
	.Imm	(ImmReg),
	.UseImm	(UseImm),
	.SEL	(ALUsel),
        .ALUout (ALUout)
);

// Quick compare logic
qc	QC	
(
	.RSbus	(RSbypass), //mux
	.RTbus	(RTbypass),
	.QCsel	(QCsel),
        .Result (QCResult)
);

/*--------------------------------------------------------------------
Memory Stage  (MEM) -- Access Data Memory
---------------------------------------------------------------------*/

mem MEMcontr				// Memory access control
(
	.I3	(IR3),
        .Dread  (Dread),
     	.Dwrite (Dwrite),
  	.Dsize  (Dsize)
);

// Handle sub-word loads (ugh)
always @(Din or IR3 or Daddr) begin
   if (IR3[`op] == `LBU) begin
      loadData = (Din >> ((~Daddr & 32'h3) << 3)) & 32'hff;
   end
   else if (IR3[`op] == `LB) begin
      preLoadData = (Din >> ((~Daddr & 32'h3) << 3)) & 32'hff;
      // sign extend!
      loadData = {{24{preLoadData[7]}}, preLoadData[7:0]};
   end
   else if (IR3[`op] == `LHU) begin
        loadData = (Din >> ((( ~Daddr >> 1 ) & 32'h1)  << 4)) & 32'hffff;
   end
   else if (IR3[`op] == `LH) begin
     preLoadData = (Din >> ((( ~Daddr >> 1 ) & 32'h1)  << 4)) & 32'hffff;
     loadData = {{16{preLoadData[15]}}, preLoadData[15:0]};
   end
   else begin
      loadData = Din;
   end
//$display("%d:LoadData = %d\n", $time, loadData);
end

always @(posedge CLK) begin 
   SMDR <= `TICK RTbypass;  //mux
   MAR  <= `TICK ALUout;
   LMDR <= `TICK loadData;
   MAR2 <= `TICK MAR;
end

assign Daddr = MAR;			// Data memory address
assign Dout = SMDR;			// Data memory data out

/*--------------------------------------------------------------------
 Writeback Stage (WB) -- Write data back to register file
---------------------------------------------------------------------*/

wb WBcontr			// Register write back control
(
	.I4	(IR4),
     	.RDaddr (RDaddr),
     	.WBsel	(WBsel)
);

assign RDbus = WBreg;		// Drive RDbus with contents of WBreg

always @(MAR2 or LMDR or PC4 or WBsel)			
begin		
   // Select write back value
   case (WBsel)
	`select_wb_load : WBreg = LMDR;
	`select_wb_link : WBreg = PC4 + 32'h8;
	default :  	  WBreg = MAR2;
   endcase
end

always@(posedge CLK) begin
	//RDreg <= `TICK RDbus;
	//RDaddrReg <= `TICK RDaddr;
end

endmodule		
